package com.dgsrz.demo.game;

import android.app.Activity;
import android.graphics.PointF;
import android.renderscript.Matrix4f;
import com.dgsrz.demo.primitive.TexturedCircle;
import com.dgsrz.demo.primitive.TexturedRectangle;
import com.dgsrz.demo.util.GameUtils;
import com.dgsrz.demo.util.Spline;

import javax.microedition.khronos.opengles.GL10;
import java.util.ArrayList;

/**
 * Created by dgsrz on 15/7/3.
 */
public class Slider extends HitObjectBase {

    private static final int ANIM_SNAKE_LENGTH = 600;
    private static final int CIRCLE_SIZE       = 128;

    private String mSliderCommand;

    private Spline.CurveTypes            mCurveType;
    private ArrayList<PointF>            mSavedPathPoints;
    private ArrayList<ArrayList<PointF>> mControlPoints;

    private TexturedRectangle mRectangle;
    private TexturedCircle    mCircle;

    private float mAlpha;
    private long  mAnimStartTime;

    private volatile int mDrawnFrames;
    private volatile boolean mPrepareToDraw = true;

    public Slider(final Activity context, final GL10 gl) {
        super(context, gl);

        mCurveType = Spline.CurveTypes.Bezier;
        mControlPoints = new ArrayList<>();
        mSavedPathPoints = null;

        mRectangle = new TexturedRectangle(gl);
        mCircle = new TexturedCircle(gl, CIRCLE_SIZE / 2);

        mAnimStartTime = 0;
        mAlpha = 1f;
        mDrawnFrames = 0;
    }

    public ArrayList<PointF> getPathPoints() {
        if (mSavedPathPoints == null) {
            mSavedPathPoints = new ArrayList<>();
            for (ArrayList<PointF> points : mControlPoints) {
                mSavedPathPoints.addAll(new Spline(points, mCurveType).getPoints());
            }
        }
        return mSavedPathPoints;
    }

    @Override
    public void drawObject() {
        final GL10 gl = getGLStateManager();

        ArrayList<PointF> points = getPathPoints();
        int lastIndex = points.size() - 1;

        if (mPrepareToDraw) {
            gl.glClearColor(0, 0, 0, 0);
            gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
            mPrepareToDraw = false;
        }

        long currentMillis = System.currentTimeMillis();
        if (currentMillis - mAnimStartTime < ANIM_SNAKE_LENGTH) {
            float animPercent = 1.f * (currentMillis - mAnimStartTime) / ANIM_SNAKE_LENGTH;
            lastIndex = (int) (lastIndex * animPercent);
            setAlpha(animPercent);
        } else {
            setAlpha(1f);
        }

        float circleScale = CIRCLE_SIZE / 2f;
        Matrix4f matrix = new Matrix4f();

        // Cap on slider head
        {
            PointF current = points.get(0);
            PointF next = points.get(1);

            float x1 = current.x;
            float x2 = next.x;
            float y1 = current.y;
            float y2 = next.y;

            double theta = Math.atan2(y1 - y2, x1 - x2);
            float sine = (float) Math.sin(theta);
            float cosine = (float) Math.cos(theta);

            gl.glPushMatrix();
            matrix.loadIdentity();
            matrix.set(0, 0, cosine);
            matrix.set(0, 1, sine);
            matrix.set(1, 0, -sine);
            matrix.set(1, 1, cosine);
            matrix.set(3, 0, x1);
            matrix.set(3, 1, y1);
            gl.glLoadMatrixf(matrix.getArray(), 0);
            mCircle.drawPrimitives();
            gl.glPopMatrix();
        }

        // Draw slider body
        for (; mDrawnFrames < lastIndex; mDrawnFrames++) {
            PointF current = points.get(mDrawnFrames);
            PointF next = points.get(mDrawnFrames + 1);

            float x1 = current.x;
            float x2 = next.x;
            float y1 = current.y;
            float y2 = next.y;

            float distance = (float) Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
            float magic = circleScale / distance;

            double theta = Math.atan2(y2 - y1, x2 - x1);
            float sine = (float) Math.sin(theta);
            float cosine = (float) Math.cos(theta);

            // Rotate the world
            gl.glPushMatrix();
            matrix.loadIdentity();
            matrix.set(0, 0, x2 - x1);
            matrix.set(0, 1, y2 - y1);
            matrix.set(1, 0, -magic * (y2 - y1));
            matrix.set(1, 1, magic * (x2 - x1));
            matrix.set(3, 0, x1);
            matrix.set(3, 1, y1);
            gl.glLoadMatrixf(matrix.getArray(), 0);
            mRectangle.drawPrimitives();
            gl.glPopMatrix();

            gl.glPushMatrix();
            matrix.loadIdentity();
            matrix.set(0, 0, cosine);
            matrix.set(0, 1, sine);
            matrix.set(1, 0, -sine);
            matrix.set(1, 1, cosine);
            matrix.set(3, 0, x2);
            matrix.set(3, 1, y2);
            gl.glLoadMatrixf(matrix.getArray(), 0);
            mCircle.drawPrimitives();
            gl.glPopMatrix();
        }
    }

    public void startAnimation() {
        mAnimStartTime = System.currentTimeMillis();
        mPrepareToDraw = true;
        mDrawnFrames = 0;
    }

    private void parseControlPoint() {
        final Activity context = getContext();

        ArrayList<ArrayList<PointF>> points = new ArrayList<>();
        points.add(new ArrayList<PointF>());

        String[] data = mSliderCommand.split("[,]");

        int index = 0;
        float posX = Float.valueOf(data[0]);
        float posY = Float.valueOf(data[1]);
        points.get(index).add(GameUtils.getRealPos(context, new PointF(posX, posY)));

        for (String temp : data[5].split("[|]")) {
            if (temp.contains(":")) {
                data = temp.split("[:]");
                posX = Float.valueOf(data[0]);
                posY = Float.valueOf(data[1]);

                PointF point = GameUtils.getRealPos(context, new PointF(posX, posY));
                PointF lastPoint = points.get(index).get(points.get(index).size() - 1);

                if (point.x == lastPoint.x && point.y == lastPoint.y) {
                    points.add(new ArrayList<PointF>());
                    index++;
                }
                points.get(index).add(point);
            } else {
                mCurveType = Spline.getCurveType(temp.charAt(0));
            }
        }
        mControlPoints = points;
    }

    public String getSliderCommand() {
        return this.mSliderCommand;
    }

    public void setSliderCommand(String sliderCommand) {
        this.mSliderCommand = sliderCommand;
        parseControlPoint();
    }

    public final float getAlpha() {
        return mAlpha;
    }

    public void setAlpha(float alpha) {
        this.mAlpha = alpha;
    }
}
